﻿Imports System.Net.Sockets

Public Class ConnectedClient

#Region " Declarations "
    Private myClient As System.Net.Sockets.TcpClient
    Private myPosition As Position
    Private myUsername As String
    Private myParentServer As TCPServer
    Private myReadThread As System.Threading.Thread
    Private Const MESSAGE_DELIMITER As Char = ControlChars.Cr
    Public Event dataReceived(ByVal sender As ConnectedClient, ByVal message As String)
    Private _WaitHandle_ClientComplete As New System.Threading.AutoResetEvent(False)
#End Region

#Region " Constructors "

    Sub New(ByVal client As System.Net.Sockets.TcpClient, ByVal parentServer As TCPServer)
        myParentServer = parentServer
        myClient = client
        myUsername = String.Empty

        myReadThread = New System.Threading.Thread(AddressOf doRead)
        myReadThread.IsBackground = True
        myReadThread.Start()
    End Sub

#End Region

#Region " Destruction "
    'TODO close out connection gracefully
    Public Sub Close()
        Try
            myClient.Client.Shutdown(SocketShutdown.Both)
        Catch ex As Exception
            'object is probably already disposed
        End Try
        Try
            myClient.Close()
        Catch ex As Exception
            'object is probably already disposed
        End Try
        Try
            myParentServer.RemoveClient(Me)
        Catch ex As Exception

        End Try
    End Sub

#End Region

#Region " Properties "

    Public Property Username() As String
        Get
            Return myUsername
        End Get
        Set(ByVal value As String)
            myUsername = value
        End Set
    End Property

    ''' <summary>
    ''' Position client joined the server
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Position As String
        Get
            Return myPosition
        End Get
        Set(ByVal value As String)
            myPosition = value
        End Set
    End Property

#End Region

#Region " Read/Send Functions "

    ''' <summary>
    ''' Blocking read callback.
    ''' Sends lines to messageReceived in TCP Server class separated by cr
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub doRead()
        Const BYTES_TO_READ As Integer = 255
        Dim readBuffer(BYTES_TO_READ) As Byte
        Dim bytesRead As Integer
        Dim sBuilder As New System.Text.StringBuilder
        Try
            Do

                bytesRead = myClient.GetStream.Read(readBuffer, 0, BYTES_TO_READ)
                If (bytesRead > 0) Then
                    Dim message As String = System.Text.Encoding.UTF8.GetString(readBuffer, 0, bytesRead)
                    If (message.IndexOf(MESSAGE_DELIMITER) > -1) Then

                        Dim subMessages() As String = message.Split(MESSAGE_DELIMITER)

                        'The first element in the subMessages string array must be the last part of the current message.
                        'So we append it to the StringBuilder and raise the dataReceived event
                        sBuilder.Append(subMessages(0))
                        RaiseEvent dataReceived(Me, sBuilder.ToString)
                        sBuilder = New System.Text.StringBuilder

                        'If there are only 2 elements in the array, we know that the second one is an incomplete message,
                        'though if there are more then two then every element inbetween the first and the last are complete messages:
                        If subMessages.Length = 2 Then
                            sBuilder.Append(subMessages(1))
                        Else
                            For i As Integer = 1 To subMessages.GetUpperBound(0) - 1
                                RaiseEvent dataReceived(Me, subMessages(i))
                            Next
                            sBuilder.Append(subMessages(subMessages.GetUpperBound(0)))
                        End If
                    Else

                        'MESSAGE_DELIMITER was not found in the message, so we just append everything to the stringbuilder.
                        sBuilder.Append(message)
                    End If
                Else
                    'bytes read should be greater than 0
                    Me.Close()
                End If
            Loop
        Catch
            Me.Close()
        End Try

    End Sub

    Public Sub SendMessage(ByVal msg As String)
        Dim sw As IO.StreamWriter
        Try
            SyncLock myClient.GetStream
                sw = New IO.StreamWriter(myClient.GetStream) 'Create a new streamwriter that will be writing directly to the networkstream.
                sw.Write(msg & vbCr)
                sw.Flush()
                _WaitHandle_ClientComplete.WaitOne()
            End SyncLock
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
            Me.Close()
        End Try
        'As opposed to writing to a file, we DONT call close on the streamwriter, since we dont want to close the stream.
    End Sub

    Public Sub SendMessageAsync(ByVal msg As String)
        Dim sw As IO.StreamWriter
        Try
            SyncLock myClient.GetStream
                sw = New IO.StreamWriter(myClient.GetStream) 'Create a new streamwriter that will be writing directly to the networkstream.
                sw.Write(msg & vbCr)
                sw.Flush()
            End SyncLock
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
            Me.Close()
        End Try
        'As opposed to writing to a file, we DONT call close on the streamwriter, since we dont want to close the stream.
    End Sub

#End Region

End Class